﻿using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using DGen;

namespace Tab2Sql
{
    /// <summary>
    /// various stats about text block
    /// </summary>
    public class Series
    {
        private readonly double[] myNumbers = new double[3000];
        private readonly string[] mySeries = new string[3000];
        //private readonly string[] mySeriesSlim = new string[3000];
        private readonly string[] notPostCodes;

        /// <summary>
        /// Simple number of elements.
        /// </summary>
        public int Count = -1;

        /// <summary>
        /// Simple string of all duplicated elements. Basic concatenation.
        /// </summary>
        public string Dupes = "";

        /// <summary>
        /// text output giving summary of input in readable format.
        /// </summary>
        public StringBuilder finalMessage = new StringBuilder();

        /// <summary>
        /// 
        /// </summary>
        public Series (string input)
        {
            mySeries = input.Split ('\n');
            CopyArraysSafely (ref myNumbers, mySeries);
            Count = getCount ();
            notPostCodes = new string[Count];
            TextAnalysis ();
        }

        public Series (string input, bool isBlockOfText)
        {

            char[] delimiters = new char[] { '\r',
                '\n',
                ','
                , '.'
                , '-'
                , ' ' };

            mySeries = input.Split (delimiters, StringSplitOptions.RemoveEmptyEntries);



            CopyArraysSafely (ref myNumbers, mySeries);
            Count = getCount ();
            notPostCodes = new string[Count];
            TextAnalysis ();
            WordStats wsx = new WordStats (input);
            wsx.WordAnalysis();
            finalMessage.AppendLine (wsx.EndString.ToString ());


        }


        /// <summary>
        /// Largest integer.
        /// </summary>
        public int Largest
        {
            get
            {
                int Biggest = 0;
                foreach (int x in myNumbers)
                {
                    if (x > Biggest)
                    {
                        Biggest = x;
                    }
                }
                return Biggest;
            }
        }

        /// <summary>
        /// Smallest double.
        /// </summary>
        public double Smallest
        {
            get
            {
                double Smallest = 999999999;
                foreach (double x in myNumbers)
                {
                    if (x < Smallest && x != 0)
                    {
                        Smallest = x;
                    }
                }
                return Smallest;
            }
        }

        /// <summary>
        /// Mean average of numbers
        /// </summary>
        public double Mean
        {
            get { return getAverageFromDoubleArray(myNumbers); }
        }

        /// <summary>
        /// Unique elements in list.
        /// </summary>
        public int DistinctCount
        {
            get
            {
                var tmp = new string[mySeries.Count() + 50];
                int ReturnValue = 0;
                int i = 0;
                foreach (string myValue in mySeries)
                {
                    if (myValue != null && myValue.Length > 0)
                    {
                        if (tmp.Contains(myValue))
                        {
                            //whatever
                        }
                        else
                        {
                            i++;
                            tmp[i] = myValue;
                        }
                    }
                }
                ReturnValue = i;
                return ReturnValue;
            }
        }

        /// <summary>
        /// Longest string in list.
        /// </summary>
        public string LongestLine
        {
            get
            {
                
                string ReturnValue = "";

                int CurrentRecord = 0;
                int length = 0;
                foreach (string myValue in mySeries)
                {
                    if (myValue != null && myValue.Length > 0)
                    {
                        length = myValue.Trim().Length;
                        if (length > CurrentRecord)
                        {
                            CurrentRecord = length;
                            ReturnValue = myValue;
                        }
                    }
                }


                return ReturnValue;
            }
        }

        /// <summary>
        /// Shortest string in list.
        /// </summary>
        public string ShortestLine
        {
            get
            {
                
                string ReturnValue = "";

                int CurrentRecord = 0;
                int length = 0;
                foreach (string myValue in mySeries)
                {
                    if (myValue != null && myValue.Length > 0)
                    {
                        length = myValue.Trim().Length;
                        if (length < CurrentRecord || CurrentRecord == 0)
                        {
                            CurrentRecord = length;
                            ReturnValue = myValue;
                        }
                    }
                }
                return ReturnValue;
            }
        }

        /// <summary>
        /// combined length of strings.
        /// </summary>
        public int CombinedLength
        {
            get
            {
                string foo = "";
                foreach (string c in mySeries)
                {
                    string c2 = c.Trim();
                    foo += c2;
                }
                return foo.Length;
            }
        }

        /// <summary>
        /// simple summation of all numeric elements.
        /// </summary>
        public double Total
        {
            get
            {
                double TotalX = 0;
                foreach (double x in myNumbers)
                {
                    TotalX += x;
                }

                return TotalX;
            }
        }

        private void TextAnalysis ()
        {
            finalMessage.AppendLine ("This series contains " + Count + " elements");
            finalMessage.AppendLine ("This series contains " + DistinctCount + " unique elements");
            finalMessage.AppendLine ();
            finalMessage.AppendLine ("Longest value is " + LongestLine + " ");
            finalMessage.AppendLine ("Shortest value is " + ShortestLine + " ");
            finalMessage.AppendLine ("Combined Length is  " + CombinedLength + " ");
            finalMessage.AppendLine ();
            if (Total == 0 && Mean == 0)
            {
                finalMessage.AppendLine ("No numeric values detected.");
            }
            else
            {
                finalMessage.AppendLine ("Total : " + String.Format ("{0:n}", Total));
                finalMessage.AppendLine ("Average : " + Mean);

            finalMessage.AppendLine ("Largest value is " + Largest + " ");
                finalMessage.AppendLine ("Smallest value is " + Smallest + " ");
            }

            finalMessage.AppendLine ();
            finalMessage.AppendLine ("Most common value is " + getMostFrequent () + " ");
            finalMessage.AppendLine ("Dupes list : " + Environment.NewLine + Dupes);
            finalMessage.AppendLine ();
            if (getHowManyPostCodes () > 0)
            {
                finalMessage.AppendLine ("This many post codes : " + getHowManyPostCodes ().ToString ());
            }
            finalMessage.AppendLine(Environment.NewLine);
        }

        private int getCount()
        {
            int ReturnValue = 0;
            int i = 0;
            foreach (string myValue in mySeries)
            {
                if (myValue != null && myValue.Length > 0)
                {
                    i++;
                }
            }
            ReturnValue = i;
            return ReturnValue;
        }
        /// <summary>
        /// Iterates through the values in mySeries and maintains a counter of occurances.
        /// Note:This forces everything to lower case.
        /// </summary>
        /// <returns>
        /// A <see cref="Hashtable"/>
        /// </returns>
        private Hashtable getFrequencyAnalysis ()
        {
            var Keys = new Hashtable ();

            foreach (string myValue in mySeries)
            {
                if (myValue != null && myValue.Length > 0)
                {
                    // If value is among our keys then we just add one to counter
                    if (Keys.Contains (myValue.ToLower ()))
                    {
                        Keys[myValue.ToLower ()] = (int)Keys[myValue.ToLower ()] + 1;
                    } else
                    {
                        /// it's new so we add a new entry with a count of 1.
                        Keys.Add (myValue.ToLower (), 1);
                    }
                }
            }
            return Keys;
        }

        /// <summary>
        /// Analyses hashtable supplied and tells you the highest value.
        /// </summary>
        /// <param name="Keys"> A Hashtable </param>
        /// <param name="mrPopular"> A String for writing the most popular value.</param>

        private int getMostPopularCount (Hashtable Keys, ref string mrPopular)
        {
            int popular = 0;
            foreach (string K in Keys.Keys)
            {
                if ((int)Keys[K.ToLower ()] > popular)
                {
                    popular = (int)Keys[K.ToLower ()];
                    mrPopular = K.ToLower ();
                }
                if ((int)Keys[K] > 1)
                {
                    Dupes += K + " - " + (int)Keys[K] + " occurances" + Environment.NewLine;
                }
            }
            return popular;
        }

        private string getMostFrequent ()
        {

            string rtnValue = "";
            var Keys = getFrequencyAnalysis();
            int popular=getMostPopularCount(Keys,ref rtnValue);
            return rtnValue + " (" + popular + " occurances)";
        }

        private void CopyArraysSafely(ref double[] myNumbers, string[] mySeries)
        {
            int i = 0;
            foreach (string value in mySeries)
            {
                if (DGen.DTypes.IsStringDecimal(value))
                {
                    try
                    {
                        myNumbers[i] = Convert.ToDouble(value);
                        i++;
                    }
                    catch
                    {
                        // meh
                    }
                }
            }
            myNumbers = (double[]) DGen.DGen.ResizeArray(myNumbers, i + 1);
        }


        private double getAverageFromDoubleArray(double[] dblArray)
        {
            double dblResult = 0;
            foreach (double dblValue in dblArray)
                dblResult += dblValue;
            return dblResult/dblArray.Length;
        }

        private int getHowManyPostCodes()
        {
            int runningTotal = 0;
            foreach (string x in mySeries)
            {
                if (DGen.DText.IsValidPostCode(x))
                {
                    runningTotal++;
                }
                else
                {
                    notPostCodes[runningTotal] = x;
                }
            }
            return runningTotal;
        }
    }
}